#include "AssimpLoader.h"

#include "assimp/Importer.hpp"
#include "assimp/scene.h"
#include "assimp/anim.h"
#include "assimp/postprocess.h"
#include "assimp/vector3.h"

using namespace Assimp;
AssimpLoader::AssimpLoader()
{
}

std::shared_ptr<Scene> AssimpLoader::Load(std::string path)
{
    Importer importer;
    auto rawScene = importer.ReadFile(path, 
        aiProcess_Triangulate |
        aiProcess_FindInstances |
        aiProcess_OptimizeGraph | 
        aiProcess_GenNormals);
    if (rawScene)
    {
        auto scene = std::make_shared<Scene>();
        //loadScene(rawScene, scene);

        for (int i = 0; i < rawScene->mNumAnimations; i++) {
            auto animation = rawScene->mAnimations[0];
            auto channel = animation->mChannels[0];
            Animation anim;
            anim.mNodeName = channel->mNodeName.C_Str();
            anim.mNumKeys = channel->mNumPositionKeys;
            for (int j = 0; j < anim.mNumKeys; j++)
            {
                auto position = channel->mPositionKeys[j].mValue;
                Vector3f vPos{ position.x,position.y,position.z};
                TranslateKey translatekey{ float(channel->mPositionKeys[j].mTime),{ vPos } };
                anim.mTranslateKey.push_back(translatekey);

                auto rotate = channel->mRotationKeys[j].mValue;
                Vector4f vRotate{ rotate.x,rotate.y,rotate.z,rotate.w };
                RotateKey rotatekey{ float(channel->mRotationKeys[j].mTime),{ vRotate } };
                anim.mRotateKey.push_back(rotatekey);

                auto scaling = channel->mScalingKeys[j].mValue;
                Vector3f vScaling{ scaling.x,scaling.y,scaling.z };
                ScaleKey scalekey{ float(channel->mScalingKeys[j].mTime),{ vScaling } };
                anim.mScaleKey.push_back(scalekey);
            }
            scene->mAnimation.push_back(anim);
        }

        for (int j = 0; j < rawScene->mNumMeshes; j++)
        {
            Mesh tempMesh;
            auto mesh = rawScene->mMeshes[j];

            for (int k = 0; k < mesh->mNumVertices; k++)
            {
                auto rawVertices = mesh->mVertices[k];
                Vertex vertex;
                vertex.position = { rawVertices.x,rawVertices.y,rawVertices.z };
                tempMesh.mVertices.push_back(vertex);
            }
            for (int k = 0; k < mesh->mNumBones; k++)
            {
                auto rawBone = mesh->mBones[k];
                auto rawmat = rawBone->mOffsetMatrix;
                Bone bone;
                bone.mName = rawBone->mName.C_Str();
                bone.mCountWeight = rawBone->mNumWeights;
                bone.mOffsetMat = glm::mat4{ rawmat.a1,rawmat.a2,rawmat.a3,rawmat.a4 ,
                    rawmat.b1,rawmat.b2,rawmat.b3,rawmat.b4 ,
                    rawmat.c1,rawmat.c2,rawmat.c3,rawmat.c4 ,
                    rawmat.d1,rawmat.d2,rawmat.d3,rawmat.d4 
                };

                bone.mWeight = new Weight[rawBone->mNumWeights];
                memcpy(bone.mWeight,rawBone->mWeights, rawBone->mNumWeights*sizeof(Weight));
                tempMesh.mBones.push_back(bone);
                for (int  i = 0; i < rawBone->mNumWeights; i++)
                {
                    tempMesh.mVertices[rawBone->mWeights[i].mVertexId].weight[k] = rawBone->mWeights[i].mWeight;
                    tempMesh.mVertices[rawBone->mWeights[i].mVertexId].bone[k] = k;
                }
            }

            scene->mMesh.push_back(tempMesh);
        }

        auto node = ReadNode(rawScene->mRootNode);

        scene->mNode = node;
        return scene;
    }

    return std::shared_ptr<Scene>();
}

std::shared_ptr<Node> AssimpLoader::ReadNode(aiNode * rootNode)
{
    if (rootNode)
    {
        std::shared_ptr<Node> node = std::make_shared<Node>();
        auto transform = rootNode->mTransformation;
        node->mTransform = glm::mat4{ transform.a1,transform.a2,transform.a3,transform.a4,
            transform.b1,transform.b2,transform.b3,transform.b4,
            transform.c1,transform.c2,transform.c3,transform.c4, 
            transform.d1,transform.d2,transform.d3,transform.d4};
        node->mName = rootNode->mName.C_Str();
        for (size_t i = 0; i < rootNode->mNumChildren; i++)
        {
            auto child = ReadNode(rootNode->mChildren[i]);
            child->mParent.push_back(node.get());
            node->mChildren.push_back(child);
        }
        return node;
    }
    return nullptr;
}
